package com.bpf.base.base.log;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.bpf.base.common.Constants;
import com.bpf.base.model.system.OperateLog;
import com.bpf.base.service.system.OperateLogService;
import com.bpf.base.util.IPUtil;
import com.bpf.base.util.SpringSecurityUtil;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import java.lang.reflect.Method;
import java.util.Date;

@Aspect
@Component
public class SystemLogAspect {
	// 注入Service用于把日志保存数据库
	@Autowired
	private OperateLogService operateLogService;
	// 本地异常日志记录对象
	private static final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);

	// Service层切点
	@Pointcut("@annotation(com.bpf.base.base.log.SystemServiceLog)")
	public void serviceAspect() {
	}

	// Controller层切点
	@Pointcut("@annotation(com.bpf.base.base.log.SystemControllerLog)")
	public void controllerAspect() {
	}

	/**
	 * 前置通知 用于拦截Controller层记录用户的操作
	 * 
	 * @param joinPoint
	 *            切点
	 */
	@Before("controllerAspect()")
	public void doBefore(JoinPoint joinPoint) {

		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		try {
			// *========数据库日志=========*//
			OperateLog operateLog = new OperateLog();
			operateLog.setOperateDescription(getControllerMethodDescription(joinPoint));
			operateLog.setOperateMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
			operateLog.setOperateType(Constants.OperateLogType.OPERATE_ACTION);
			operateLog.setOperateIp(IPUtil.getIpAddr(request));
			operateLog.setOperateExceptionCode(null);
			operateLog.setOperateExceptionDetail(null);
			operateLog.setOperateRequestUrl(request.getRequestURL().toString());
			operateLog.setOperateParams(request.getQueryString());
			operateLog.setOperateCreateBy(SpringSecurityUtil.getPrincipal().getUsername());
			operateLog.setOperateCreateTime(new Date());
			// 保存数据库
			operateLogService.insertSelective(operateLog);
		} catch (Exception e) {
			// 记录本地异常日志
			logger.error("异常信息:{}", e.getMessage());
		}
	}

	/**
	 * 异常通知 用于拦截service层记录异常日志
	 * 
	 * @param joinPoint
	 * @param e
	 */
	@AfterThrowing(pointcut = "serviceAspect()", throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		try {
			/* ==========数据库日志========= */
			OperateLog operateLog = new OperateLog();
			operateLog.setOperateDescription(getServiceMthodDescription(joinPoint));
			operateLog.setOperateExceptionCode(e.getClass().getName());
			operateLog.setOperateType(Constants.OperateLogType.OPERATE_EXCEPTION);
			operateLog.setOperateExceptionDetail(e.getMessage());
			operateLog.setOperateMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
			operateLog.setOperateRequestUrl(request.getRequestURL().toString());
			operateLog.setOperateParams(request.getQueryString());
			operateLog.setOperateCreateBy(SpringSecurityUtil.getPrincipal().getUsername());
			operateLog.setOperateCreateTime(new Date());
			operateLog.setOperateIp(IPUtil.getIpAddr(request));
			// 保存数据库
			operateLogService.insertSelective(operateLog);
		} catch (Exception ex) {
			// 记录本地异常日志
			logger.error("异常信息:{}", ex.getMessage());
		}
	}

	/**
	 * 获取注解中对方法的描述信息 用于service层注解
	 * 
	 * @param joinPoint
	 *            切点
	 * @return 方法描述
	 * @throws Exception
	 */
	public static String getServiceMthodDescription(JoinPoint joinPoint) throws Exception {
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		String description = "";
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					description = method.getAnnotation(SystemServiceLog.class).description();
					break;
				}
			}
		}
		return description;
	}

	/**
	 * 获取注解中对方法的描述信息 用于Controller层注解
	 * 
	 * @param joinPoint
	 *            切点
	 * @return 方法描述
	 * @throws Exception
	 */
	public static String getControllerMethodDescription(JoinPoint joinPoint) throws Exception {
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		String description = "";
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					description = method.getAnnotation(SystemControllerLog.class).description();
					break;
				}
			}
		}
		return description;
	}
}
